#define _CRT_SECURE_NO_WARNINGS
#define _CRT_NON_CONFORMING_SWPRINTFS
#include <WinSock2.h>
#include <Windows.h>
#include <windowsx.h>
#include <cstdio>
#include <stdio.h>
#include <time.h>
#include <conio.h>
#include <TlHelp32.h>
#include <direct.h>
#include <ShellAPI.h>
#include <Psapi.h>
#include <dwmapi.h>
#include <ddraw.h>
#include <gdiplus.h>
#include <cmath>
#include <winternl.h>

#include <d3d9.h>
#include <d3dx9.h>
#include <d3d11.h>
#include <d3d10.h>
#include <D3DX10.h>
#include <intrin.h>

#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>

#pragma comment(lib, "psapi.lib")
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
#pragma comment(lib, "Dwmapi.lib")
#pragma comment(lib, "ddraw.lib")
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "gdiplus.lib")
#pragma comment(lib, "ws2_32.lib") 
#pragma comment(lib,"d3dx10.lib")

#include <Helper.h>

#include <Memory\\AsmHelper.h>
#include <Memory\\External.h>
#include <Memory\\Internal.h>

#include <Math\\Vector.h>
#include <Math\\Vector2.h>
#include <Math\\Vector4.h>
#include <Math\\Rectangle.h>
#include <Math\\Helper.h>

#include <Graphic\\GDI+HwOverlayWrapper.h>
#include <Graphic\\DxDraw.h>
#include <Graphic\\SilverMenu.h>

#include <System\\IniWrapper.h>

#include <Other\\CryptString.h>

#include <ALL_I.h>
#include "Cryptor.h"

#include "GameClasses.h"
typedef int (__stdcall* Present_t)(IDXGISwapChain*, UINT, UINT);
typedef int (__fastcall* PreFrameUpdate_t)(DWORD, DWORD, float);
#include "CDraw.h"
#include "GlobalClasses.h"
#include "Globals.h"
#include "GameUtilities.h"

bool CheckKeys()
{
	if (GetAsyncKeyState(VK_INSERT) & (1<<15))
	{
		InstantHit = !InstantHit;
		return true;
	}
	else if (GetAsyncKeyState(VK_HOME) & (1<<15))
	{
		Aimbot = !Aimbot;
		return true;
	}
	return false;
}

void OnDraw()
{
	MyGameRenderer = fb::GameRenderer::Singleton();
	MyDxRenderer = fb::DxRenderer::Singleton();
	JUNK_CODE;
	MyD3D11Device = MyDxRenderer->pDevice;
	MyD3D11DeviceContext = MyDxRenderer->pContext;
	JUNK_CODE;

	if (!MyGameRenderer || !MyDxRenderer || !MyD3D11Device || !MyD3D11DeviceContext)
	{
		return;
	}

	for (int i = 0; i < 64; i++)
	{
		if (Targets[i].IsValidPlayer)
		{
			if (Targets[i].IsValidForDrawing)
			{
				float BoxHeight = SD_Tools::MathHelper::GetDiff(Targets[i].HeadScreen.y, Targets[i].OriginScreen.y);
				float BoxWidth;
				if (Targets[i].IsTank)
				{
					BoxWidth = BoxHeight*2.0f;
				}
				else
				{
					BoxWidth = BoxHeight*0.666f;
				}
				JUNK_CODE;
				float BoxX = Targets[i].OriginScreen.x - BoxWidth/2.0f;
				float BoxY = (Targets[i].HeadScreen.y < Targets[i].OriginScreen.y) ? Targets[i].HeadScreen.y : Targets[i].OriginScreen.y;
				JUNK_CODE;
				if (Targets[i].IsEnemy)
				{
					Draw.FillRect1(Targets[i].HeadScreen.x-3.0f, Targets[i].HeadScreen.y-3.0f, 6.0f, 6.0f, Targets[i].BoxColor);

					Draw.DrawRect(BoxX, BoxY, BoxWidth, BoxHeight, Targets[i].BoxColor);
					JUNK_CODE;
					Draw.DrawEngText((int)BoxX, (int)BoxY - 10, 0xFFFFFFFF, Targets[i].PlayerName, 0.8f);
					JUNK_CODE;
					Draw.DrawEngText((int)BoxX, (int)(BoxY + BoxHeight) + 2, 0xFFFFFFFF, Targets[i].HealthString, 0.8f);
				}
				else
				{
					Draw.DrawRect2(BoxX, BoxY, BoxWidth, BoxHeight, Targets[i].BoxColor);
				}
			}
		}
	}

	Draw.DrawEngText(10, 10, 0xFF0000FF, /*BF3Bumerang*/ SD_Tools::CryptStringA(11, 0xF5B, 0x19BD9C4D, 0x1CA08033, 0x232C2500).c_str(), 1.4f);

	if (InstantHit)
	{
		Draw.DrawEngText(10, 30, 0xFF00FF00, /*InstantHit*/ SD_Tools::CryptStringA(10, 0x73D, 0x49F5D24E, 0x18C9A15F, 0x2B360000).c_str(), 1.4f);
	}
	if (Aimbot)
	{
		Draw.DrawEngText(10, 50, 0xFF00FFFF, /*Aimbot*/ SD_Tools::CryptStringA(6, 0x38D, 0xEFEE6542, 0x2D360000).c_str(), 1.4f);
	}

	float CX = static_cast< float >( MyDxRenderer->m_screenInfo.m_nWindowWidth ) * 0.5f;
	JUNK_CODE;
	float CY = static_cast< float >( MyDxRenderer->m_screenInfo.m_nWindowHeight ) * 0.5f;

	Draw.FillRect1(CX-1.0f, CY-15.0f, 2.0f, 30.0f, CrossColor);
	JUNK_CODE;
	Draw.FillRect1(CX-15.0f, CY-1.0f, 30.0f, 2.0f, CrossColor);
}

int __stdcall HookPresent(IDXGISwapChain* Swapchain, UINT arg1, UINT arg2)
{
	*(DWORD*) 0x235DB14 = 0;
	OnDraw();
	JUNK_CODE;
	return OrigPresent(Swapchain, arg1, arg2);
}

void ClearAllTargets()
{
	for (int i = 0; i < 64; i++)
	{
		Targets[i].IsValidPlayer = false;
		Targets[i].IsValidForDrawing = false;
	}
}

void UpdateTargets()
{
	MyGameContext = fb::ClientGameContext::Singleton();
	MyGameRenderer = fb::GameRenderer::Singleton();
	JUNK_CODE;
	MyDxRenderer = fb::DxRenderer::Singleton();

	MyPlayerManager = MyGameContext->m_clientPlayerManager;
	MyD3D11Device = MyDxRenderer->pDevice;
	JUNK_CODE;
	MyD3D11DeviceContext = MyDxRenderer->pContext;
	MyBorderInputNode = fb::BorderInputNode::Singleton();

	if (!MyPlayerManager || !MyGameContext || !MyGameRenderer || !MyDxRenderer || !MyD3D11Device || !MyD3D11DeviceContext)
	{
		ClearAllTargets();
		return;
	}

	fb::ClientPlayer* LocalClientPlayer = MyPlayerManager->m_localPlayer;

	if (!LocalClientPlayer || !HIWORD(LocalClientPlayer))
	{
		ClearAllTargets();
		return;
	}

	fb::ClientSoldierEntity* LocalClientSoldier = LocalClientPlayer->getSoldier();

	if (!LocalClientSoldier || !HIWORD(LocalClientSoldier))
	{
		ClearAllTargets();
		return;
	}

	fb::Vec3 LocalClientSoldierPosition;
	GameUtilities::GetPosition(LocalClientSoldier, &LocalClientSoldierPosition);

	if (LocalClientSoldier->m_breathControlHandler)
	{
		LocalClientSoldier->m_breathControlHandler->m_breathControlMultiplier = 0.0f;
		LocalClientSoldier->m_breathControlHandler->m_breathControlTimer = 0.0f;
		LocalClientSoldier->m_breathControlHandler->m_breathControlPenaltyTimer = 0.0f;
		LocalClientSoldier->m_breathControlHandler->m_breathControlPenaltyMultiplier = 0.0f;
	}

	fb::Vec3 LocalEyePos;
	MyGameRenderer->m_viewParams.view.Update();
	GameUtilities::GetOriginFromMatrix(&(MyGameRenderer->m_viewParams.view.m_viewMatrixInverse), &LocalEyePos);
	/*if (!GameUtilities::GetBonePos(LocalClientSoldier, &LocalEyePos, fb::BoneCollisionComponent::Bone_Head))
	{
		if (!GameUtilities::GetBonePos(LocalClientSoldier, &LocalEyePos, fb::BoneCollisionComponent::Bone_Pelvis))
		{
			LocalEyePos = LocalClientSoldierPosition;
		}
	}*/

	fb::ClientSoldierWeapon* LocalSoldierWeapon = GameUtilities::GetCurrentWeapon(LocalClientSoldier);
	
	int TargetId = -1;
	float CurDist = 999999.9f;
	for (int i = 0; i < 64; i++)
	{
		fb::ClientPlayer* CurrentClientPlayer = MyPlayerManager->m_idToPlayerMap[i];

		if (!CurrentClientPlayer || CurrentClientPlayer == LocalClientPlayer || IsBadReadPtr(CurrentClientPlayer, 4) || !HIWORD(CurrentClientPlayer) || CurrentClientPlayer->m_id != i)
		{
			Targets[i].IsValidForDrawing = false;
			Targets[i].IsValidPlayer = false;
			continue;
		}
		fb::ClientSoldierEntity* CurrentSoldierEnt = CurrentClientPlayer->getSoldier();
		if (!CurrentSoldierEnt || CurrentSoldierEnt->m_health <= 0.0f)
		{
			Targets[i].IsValidForDrawing = false;
			Targets[i].IsValidPlayer = false;
			continue;
		}
		Targets[i].IsValidPlayer = true;

		if (CurrentSoldierEnt->m_soldierWeaponsComponent)
		{
			CurrentSoldierEnt->m_soldierWeaponsComponent->m_weaponLightDisabled = TRUE;
		}

		if (CurrentSoldierEnt->m_replicatedController)
		{
			Targets[i].Flags = CurrentSoldierEnt->m_replicatedController->m_state.pose;
		}
		else
		{
			Targets[i].Flags = -1;
		}

		Targets[i].Health = CurrentSoldierEnt->m_health;

		sprintf(Targets[i].HealthString, /*HP: %.02f*/ SD_Tools::CryptStringA(9, 0x2E3, 0xC3BB324A, 0xC4885B1C, 0x24000000).c_str(), Targets[i].Health);

		GameUtilities::GetPosition(CurrentSoldierEnt, &Targets[i].Origin);

		Targets[i].IsTank = false;
		GameUtilities::GetBonePos(CurrentSoldierEnt, &Targets[i].Pelvis, fb::BoneCollisionComponent::Bone_Pelvis);
		Targets[i].HeadValid = GameUtilities::GetBonePos(CurrentSoldierEnt, &Targets[i].Head, fb::BoneCollisionComponent::Bone_Head);
		if (!Targets[i].HeadValid)
		{
			Targets[i].HeadValid = GameUtilities::GetBonePos(CurrentSoldierEnt, &Targets[i].Head, fb::BoneCollisionComponent::Bone_Torso);
			if (!Targets[i].HeadValid)
			{
				Targets[i].Head = Targets[i].Origin;
				Targets[i].Head += 3.0f;
				Targets[i].HeadValid = true;
				Targets[i].IsTank = true;
				strcpy(Targets[i].HealthString, /*TANK*/ SD_Tools::CryptStringA(4, 0x804, 0x4FC9C25C).c_str());
			}
		}

		if (CurrentClientPlayer->m_name.GetString())
		{
			memcpy(Targets[i].PlayerName, CurrentClientPlayer->m_name.GetString(), 50);
			Targets[i].PlayerName[49] = '\0';
		}
		else
		{
			strcpy(Targets[i].PlayerName, /*NONAME*/ SD_Tools::CryptStringA(6, 0x133, 0x72CEFD4F, 0x0F070000).c_str());
		}

		Targets[i].IsEnemy = LocalClientPlayer->m_teamId != CurrentClientPlayer->m_teamId;

		//Targets[i].IsVisible = CurrentSoldierEnt->m_isOccluded == 0;
		Targets[i].IsVisible = GameUtilities::IsVisible(&LocalEyePos, &Targets[i].Head);

		if (Targets[i].IsEnemy)
		{
			if (Targets[i].IsVisible)
			{
				Targets[i].BoxColor = 0xFF0000FF;
			}
			else
			{
				Targets[i].BoxColor = 0xFF0060FF;
			}
		}
		else
		{
			if (Targets[i].IsVisible)
			{
				Targets[i].BoxColor = 0x7700FF00;
			}
			else
			{
				Targets[i].BoxColor = 0x77FF0000;
			}
		}

		if (Targets[i].HeadValid && GameUtilities::WorldToScreen(&Targets[i].Origin, &Targets[i].OriginScreen) && GameUtilities::WorldToScreen(&Targets[i].Head, &Targets[i].HeadScreen))
		{
			Targets[i].IsValidForDrawing = true;
		}
		else
		{
			Targets[i].IsValidForDrawing = false;
		}

		Targets[i].Distance = GameUtilities::GetDistance(&LocalClientSoldierPosition, &Targets[i].Origin);

		if (Targets[i].IsEnemy && !Targets[i].IsTank && Targets[i].Distance < CurDist && Targets[i].IsVisible)
		{
			CurDist = Targets[i].Distance;
			TargetId = i;
		}
	}

	//////////////////////////////////////////////////////////////////////////

	if (LocalSoldierWeapon && HIWORD(LocalSoldierWeapon))
	{
		if (LocalSoldierWeapon->m_authorativeAiming) // Nosway && Aimbot
		{
			if (LocalSoldierWeapon->m_authorativeAiming->m_data) // Nosway
			{
				if (LocalSoldierWeapon->m_authorativeAiming->m_data->m_zoomLevels[LocalSoldierWeapon->m_authorativeAiming->m_zoomLevel])
				{
					LocalSoldierWeapon->m_authorativeAiming->m_data->m_zoomLevels[LocalSoldierWeapon->m_authorativeAiming->m_zoomLevel]->m_swayPitchMultiplier = 0.0f;
					LocalSoldierWeapon->m_authorativeAiming->m_data->m_zoomLevels[LocalSoldierWeapon->m_authorativeAiming->m_zoomLevel]->m_swayYawMultiplier = 0.0f;
					LocalSoldierWeapon->m_authorativeAiming->m_data->m_zoomLevels[LocalSoldierWeapon->m_authorativeAiming->m_zoomLevel]->m_dispersionMultiplier = 0.0f;
					LocalSoldierWeapon->m_authorativeAiming->m_data->m_zoomLevels[LocalSoldierWeapon->m_authorativeAiming->m_zoomLevel]->m_recoilMultiplier = 0.0f;
					LocalSoldierWeapon->m_authorativeAiming->m_data->m_zoomLevels[LocalSoldierWeapon->m_authorativeAiming->m_zoomLevel]->m_recoilFovMultiplier = 0.0f;
				}
			}

			if (Aimbot) // Aimbot
			{
				if (TargetId != -1)
				{
					Targets[TargetId].BoxColor = 0xFFFF00FF;
					CrossColor = 0xFFFF00FF;
					if ((GetAsyncKeyState(VK_XBUTTON1) & (1<<15)) && LocalSoldierWeapon->m_authorativeAiming->m_fpsAimer)
					{
						fb::Vec3 delta = Targets[TargetId].Head - LocalEyePos;
						float aimpitch;
						float aimyaw;

						aimpitch = atan2f(-delta.y, sqrt((delta.x * delta.x) + (delta.z * delta.z)));
						aimyaw = atan2f(delta.x, delta.z);

						LocalSoldierWeapon->m_authorativeAiming->m_fpsAimer->m_yaw = -aimyaw;
						LocalSoldierWeapon->m_authorativeAiming->m_fpsAimer->m_pitch = -aimpitch;

						if (MyBorderInputNode && MyBorderInputNode->m_inputCache)
						{
							MyBorderInputNode->m_inputCache->flInputBuffer[fb::ConceptFire] = 1.0f;
						}
					}
				}
				else
				{
					CrossColor = 0xBB0000FF;
				}
			}

		}

		fb::ClientWeapon* LocalSoldierClientWeapon = LocalSoldierWeapon->m_weapon;
		if (LocalSoldierClientWeapon && HIWORD(LocalSoldierClientWeapon)) // Norecoil/NoSpread
		{
			fb::WeaponFiring* LocalSoldierWeaponFiring = LocalSoldierWeapon->m_predictedFiring;
			if (LocalSoldierWeaponFiring)
			{
				fb::GunSway* CurrentSway = (fb::GunSway*) LocalSoldierWeaponFiring->m_weaponSway;
				if (CurrentSway)
				{
					CurrentSway->m_currentRecoilDeviation.m_pitch = 0.0f;
					CurrentSway->m_currentRecoilDeviation.m_yaw = 0.0f;
					CurrentSway->m_currentRecoilDeviation.m_roll = 0.0f;
					CurrentSway->m_currentRecoilDeviation.m_transY = 0.0f;
					JUNK_CODE;
					CurrentSway->m_dispersionAngle = 0.0f;
					CurrentSway->m_minDispersionAngle = 0.0f;
					CurrentSway->m_currentGameplayDeviationScaleFactor = 0.000001f;
					JUNK_CODE;
					CurrentSway->m_currentVisualDeviationScaleFactor = 0.000001f;
					CurrentSway->m_randomAngle = 0.0f;
					CurrentSway->m_randomRadius = 0.0f;
				}
			}

			if (InstantHit) // Instanthit
			{
				if (LocalSoldierClientWeapon->m_firingData)
				{
					fb::FiringFunctionData* MyFiringFunctionData = LocalSoldierClientWeapon->m_firingData->m_primaryFire;
					if (MyFiringFunctionData && HIWORD(MyFiringFunctionData))
					{
						if (MyFiringFunctionData->m_shot.m_projectileData && MyFiringFunctionData->m_shot.m_projectileData->getTypeInfo() && MyFiringFunctionData->m_shot.m_projectileData->getTypeInfo()->m_pInfoData)
						{
							if (strcmp(MyFiringFunctionData->m_shot.m_projectileData->getTypeInfo()->m_pInfoData->name, /*BulletEntityData*/ SD_Tools::CryptStringA(16, 0xA42, 0x2EE5B448, 0x3B85A024, 0x11838E0C, 0x1D5A9BEB).c_str()) == 0)
							{
								MyFiringFunctionData->m_shot.m_projectileData->m_gravity = 0.0f;
								MyFiringFunctionData->m_shot.m_projectileData->m_initialSpeed = 9999.0f;
								MyFiringFunctionData->m_shot.m_projectileData->m_startDamage = 9999.0f;
								MyFiringFunctionData->m_shot.m_projectileData->m_endDamage = 9999.0f;
								MyFiringFunctionData->m_shot.m_projectileData->m_instantHit = true;
							}
						}
					}
				}
			}
		}
	}

	//////////////////////////////////////////////////////////////////////////
}

int __fastcall HookPreFrameUpdate(DWORD ThisPtr, DWORD _UselessEDX, float flDeltaTime)
{
	*(DWORD*) 0x235DB14 = 0;
	JUNK_CODE;
	int Rslt = OrigPreFrameUpdate(ThisPtr, _UselessEDX, flDeltaTime);
	JUNK_CODE;
	UpdateTargets();
	*(DWORD*) 0x235DB14 = 0;
	JUNK_CODE;
	return Rslt;
}

DWORD WINAPI HackThread(void* Param)
{
	//////////////////////////////////////////////////////////////////////////

	//SD_Tools::Helper::CreateDebugConsole(MyInstance, /*DebugConsole*/ SD_Tools::CryptStringW(12, 0x1E9, 0xE9E46845, 0xFCCD7B5A, 0x0FAC8E08, 0x2348A1C9, 0x3632B4AE, 0x49F1C878).w_str());

	//////////////////////////////////////////////////////////////////////////

	while (!fb::BorderInputNode::Singleton() || !fb::DxRenderer::Singleton())
	{
		Sleep(100);
	}

	fb::BorderInputNode* MyBorderInputNode = fb::BorderInputNode::Singleton();
	DWORD* MyBorderInputNodeVTable = SD_Tools::Internal::GetVTableByObject(MyBorderInputNode);
	DWORD NumOfFunctions = SD_Tools::Internal::GetVTableFunctionCount(MyBorderInputNodeVTable);
	DWORD* MyNewBorderInputNodeVTable = new DWORD[NumOfFunctions];
	SD_Tools::Internal::CloneVTable(MyNewBorderInputNodeVTable, MyBorderInputNodeVTable, NumOfFunctions);
	OrigPreFrameUpdate = (PreFrameUpdate_t) SD_Tools::Internal::DetourVTable(MyNewBorderInputNodeVTable, (DWORD) &HookPreFrameUpdate, 27);
	SD_Tools::Internal::ReplaceObjectVTable(MyBorderInputNode, MyNewBorderInputNodeVTable);

	fb::DxRenderer* MyDxRenderer = fb::DxRenderer::Singleton();
	IDXGISwapChain* MySwapChain = MyDxRenderer->pSwapChain;
	DWORD* MySwapChainVTable = SD_Tools::Internal::GetVTableByObject(MySwapChain);
	NumOfFunctions = SD_Tools::Internal::GetVTableFunctionCount(MySwapChainVTable);
	DWORD* MyNewSwapChainVTable = new DWORD[NumOfFunctions];
	SD_Tools::Internal::CloneVTable(MyNewSwapChainVTable, MySwapChainVTable, NumOfFunctions);
	OrigPresent = (Present_t) SD_Tools::Internal::DetourVTable(MyNewSwapChainVTable, (DWORD) &HookPresent, 8);
	SD_Tools::Internal::ReplaceObjectVTable(MySwapChain, MyNewSwapChainVTable);
	
	//////////////////////////////////////////////////////////////////////////
	while (true)
	{
		*(DWORD*) 0x235DB14 = 0;
		if (CheckKeys())
		{
			Sleep(250);
		}
		Sleep(10);
	}
	//////////////////////////////////////////////////////////////////////////
	return 0;
}

BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD dwReason, LPCVOID lpReserved)
{
	switch(dwReason)
	{
	case DLL_PROCESS_ATTACH:
		SD_Tools::Helper::StartThread(HackThread);
		JUNK_CODE;
		break;
	case DLL_PROCESS_DETACH:
		ExitProcess(0);
		break;
	}
	return TRUE;
}
